﻿#include "HttpClient.h"
#include <iostream>
#include <algorithm>
#include <cctype>
#include <sstream>

static inline std::string toLowerTrim(const std::string& s) {
    std::string out;
    out.reserve(s.size());
    for (char c : s) out.push_back((char)std::tolower(static_cast<unsigned char>(c)));
    // trim both ends
    size_t start = out.find_first_not_of(" \t\r\n");
    size_t end = out.find_last_not_of(" \t\r\n");
    if (start == std::string::npos) return "";
    return out.substr(start, end - start + 1);
}

// ---------- HttpClient 实现 ----------
HttpClient& HttpClient::getInstance() {
    static HttpClient instance;
    return instance;
}

HttpClient::HttpClient() {
    // 全局初始化，只需一次
    CURLcode c = curl_global_init(CURL_GLOBAL_DEFAULT);
    if (c != CURLE_OK) {
        std::cerr << "curl_global_init failed: " << curl_easy_strerror(c) << std::endl;
    }
}

HttpClient::~HttpClient() {
    curl_global_cleanup();
}

size_t writeBodyCallback(char* ptr, size_t size, size_t nmemb, void* userdata) {
    size_t realSize = size * nmemb;
    if (userdata) {
        std::string* body = static_cast<std::string*>(userdata);
        body->append(ptr, realSize);
    }
    return realSize;
}

size_t headerCallback(char* buffer, size_t size, size_t nitems, void* userdata) {
    size_t realSize = size * nitems;
    if (!userdata) return realSize;
    std::map<std::string, std::string>* headers =
            static_cast<std::map<std::string, std::string>*>(userdata);

    // header 行通常形如 "Key: value\r\n" 或是状态行 "HTTP/1.1 200 OK\r\n"
    std::string headerLine(buffer, realSize);
    // 移除末尾的 CRLF
    while (!headerLine.empty() && (headerLine.back() == '\r' || headerLine.back() == '\n'))
        headerLine.pop_back();

    auto colonPos = headerLine.find(':');
    if (colonPos != std::string::npos) {
        std::string key = headerLine.substr(0, colonPos);
        std::string value = headerLine.substr(colonPos + 1);
        // trim spaces
        auto ltrim = [](std::string &s){
            size_t p = s.find_first_not_of(" \t");
            if (p != std::string::npos) s = s.substr(p);
            else s.clear();
        };
        auto rtrim = [](std::string &s){
            size_t p = s.find_last_not_of(" \t");
            if (p != std::string::npos) s = s.substr(0, p + 1);
            else s.clear();
        };
        ltrim(key); rtrim(key);
        ltrim(value); rtrim(value);
        headers->emplace(toLowerTrim(key), value);
    }
    return realSize;
}

HttpResponse HttpClient::performRequest(const std::string& method,
                                        const std::string& url,
                                        const std::string* body,
                                        const std::map<std::string, std::string>& request_headers,
                                        long timeout_seconds) {
    HttpResponse resp;

    CURL* curl = curl_easy_init();
    if (!curl) {
        resp.error = "curl_easy_init failed";
        return resp;
    }

    // 设置 URL
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

    // 设置超时（秒）
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout_seconds);

    // follow redirects
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

    // 设置写回调（响应体）
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeBodyCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp.body);

    // 设置头回调以捕获响应头
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerCallback);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &resp.headers);

    // 构造请求头列表
    CurlSlist curlHeaders;
    for (const auto& kv : request_headers) {
        std::string line = kv.first + ": " + kv.second;
        curlHeaders.append(line);
    }

    if (!curlHeaders.list) {
        // nothing; OK
    } else {
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curlHeaders.list);
    }

    // 支持方法
    if (method == "GET") {
        curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
    } else if (method == "POST") {
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        if (body) {
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body->c_str());
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body->size());
        } else {
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "");
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L);
        }
    } else {
        // 其他 method（如 PUT/DELETE）：使用 CUSTOMREQUEST
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());
        if (body) {
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body->c_str());
            curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body->size());
        }
    }

    // 可选：禁用信号处理（线程中使用时推荐）
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

    // 执行请求
    char curlErrBuf[CURL_ERROR_SIZE];
    curlErrBuf[0] = 0;
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curlErrBuf);

    CURLcode res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        // 出错
        if (curlErrBuf[0]) resp.error = curlErrBuf;
        else resp.error = curl_easy_strerror(res);
    }

    // HTTP 状态码
    long http_code = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
    resp.status_code = http_code;

    // 清理
    curl_easy_cleanup(curl);
    // curlHeaders 在析构时会自动释放

    return resp;
}

HttpResponse HttpClient::get(const std::string& url,
                             const std::map<std::string, std::string>& request_headers,
                             long timeout_seconds) {
    return performRequest("GET", url, nullptr, request_headers, timeout_seconds);
}

HttpResponse HttpClient::post(const std::string& url,
                              const std::string& body,
                              const std::map<std::string, std::string>& request_headers,
                              long timeout_seconds) {
    return performRequest("POST", url, &body, request_headers, timeout_seconds);
}

HttpResponse HttpClient::postJson(const std::string& url,
                                  const std::string& json,
                                  const std::map<std::string, std::string>& extra_headers,
                                  long timeout_seconds) {
    // 默认加上 Content-Type: application/json，且允许用户覆盖或添加其他 header
    std::map<std::string, std::string> headers = extra_headers;
    bool hasCt = false;
    for (const auto &kv : headers) {
        std::string k = toLowerTrim(kv.first);
        if (k == "content-type") { hasCt = true; break; }
    }
    if (!hasCt) headers.emplace("Content-Type", "application/json");
    return post(url, json, headers, timeout_seconds);
}
